// ----------------------------------------
// Filename: I2C.c
// Description: Routines to implement an MSSP I2C
// Author: Anton Jatmiko
// Date: 19 April 2014

#include "I2C.h"

// ----------------------------------------
// I2C_Init
//
// Initialize I2C MSSP
// Input:
//   none
// Output:
//   none
// Conditions:
//   none
void I2C_Init(void)
{
    TRISCbits.RC3 = 1;              // Set SCL1 to input
    TRISCbits.RC4 = 1;              // Set SDA1 to input

    SSP1STAT = 0;
    SSP1CON1 = 0;
    SSP1CON2 = 0;


    SSP1STATbits.SMP = 1;           // Slew rate control disabled
    SSP1STATbits.CKE = 0;           // Enable SMBus specific inputs

    SSP1CON1 = 0x08;                // SSPEN = 0, Disable I2C ports
                                    // SSPM = 8, Master mode, clock = Fosc / (4 * (SSP1ADD + 1))
    SSP1ADD = 0x09;                 // Fosc / 4 = 1MHz, Fscl = 100kHz

    SSP1CON1bits.SSPEN = 1;         // Enable I2C

    PIE2bits.BCL1IE = 0;

    IPR1bits.SSP1IP = 1;
    PIR1bits.SSP1IF = 0;            // Reset MSSP 1 Interrupt Flag
    PIE1bits.SSP1IE = 0;            // Disable MSSP 1 Interrupt
}

// ----------------------------------------
// I2C_Busy
//
// Check the status of the MSSP
// Input:
//   none
// Output:
//   TRUE if busy
// Conditions:
//   none
BOOL I2C_Busy(void)
{
    return (SSP1CON2bits.SEN | SSP1CON2bits.RSEN | SSP1CON2bits.PEN |
            SSP1CON2bits.RCEN | SSP1CON2bits.ACKEN | SSP1STATbits.RW);
}

// ----------------------------------------
// I2C_Start
//
// Send a start bit
// Input:
//   none
// Output:
//   TRUE if sent
// Conditions:
//   none
BOOL I2C_Start(void)
{
    if (!I2C_Busy())
    {
        SSP1CON2bits.SEN = 1;
        return bTRUE;
    }
    return bFALSE;
}

// ----------------------------------------
// I2C_Stop
//
// Send a stop bit
// Input:
//   none
// Output:
//   TRUE if sent
// Conditions:
//   none
BOOL I2C_Stop(void)
{
    if (!I2C_Busy())
    {
        SSP1CON2bits.PEN = 1;
        return bTRUE;
    }
    return bFALSE;
}

// ----------------------------------------
// I2C_Restart
//
// Send a restart bit
// Input:
//   none
// Output:
//   TRUE if sent
// Conditions:
//   none
BOOL I2C_Restart(void)
{
    if (!I2C_Busy())
    {
        SSP1CON2bits.RSEN = 1;
        return bTRUE;
    }
    return bFALSE;
}

// ----------------------------------------
// I2C_ACK
//
// Send an acknowledge bit
// Input:
//   none
// Output:
//   TRUE if sent
// Conditions:
//   none
BOOL I2C_ACK(void)
{
    if (!I2C_Busy())
    {
        SSP1CON2bits.ACKDT = 0;
        SSP1CON2bits.ACKEN = 1;
        return bTRUE;
    }
    return bFALSE;
}

// ----------------------------------------
// I2C_NAK
//
// Send an non-acknowledge bit
// Input:
//   none
// Output:
//   TRUE if sent
// Conditions:
//   none
BOOL I2C_NAK(void)
{
    if (!I2C_Busy())
    {
        SSP1CON2bits.ACKDT = 1;
        SSP1CON2bits.ACKEN = 1;
        return bTRUE;
    }
    return bFALSE;
}

// ----------------------------------------
// I2C_Busy
//
// Transmit data to slave
// Input:
//   data is the data to be trasmitted
// Output:
//   TRUE if transmitted
// Conditions:
//   none
BOOL I2C_Transmit(UINT8 data)
{
    if (!I2C_Busy())
    {
        SSP1CON1bits.WCOL = 0;
        SSP1BUF = data;

        if (!SSP1CON1bits.WCOL)
            return bTRUE;
    }
    return bFALSE;
}

// ----------------------------------------
// I2C_Receive
//
// Receive the data from slave
// Input:
//   data is a pointer to the received data
// Output:
//   TRUE if received
// Conditions:
//   none
BOOL I2C_Receive(UINT8 * const dataPtr)
{
    static BOOL rcenSet = 0;

    if (!I2C_Busy())
    {
        if (SSP1STATbits.BF)
        {
            *dataPtr = SSP1BUF;
            return bTRUE;
        }
        else
            SSP1CON2bits.RCEN = 1;
    }
    return bFALSE;
}